BitBastelei #234 – WS2812B (Neopixel) Uhr

BitBastelei #234 - WS2812B (Neopixel) Uhr

(132 MB) 00:38:13

2017-03-12 11:00 🛈

Die WS2812B, auch bekannt als Neopixel, sind RGB-LEDs mit internem Steuer-IC, welche sich über einen einzelnen Datenpin kaskadieren und separat ansteuern lassen. Als in meinem Feed ein Angebot für ¼-Kreise mit 15 dieser LEDs durchtickerte war der Plan schnell klar: 4 ¼-Kreise á 15 LEDs macht 60 LEDs. Passend für eine Uhr. Mal schauen, ob es auch funktioniert.
Zur Zeithaltung kommt ein DS3231 zum Einsatz – ein I²C-Chip, welcher speziell für Uhren gedacht ist und genauer funktioniert als die internen Taktgeber der üblichen µCs.

Inhalt:

  • 00:00 LED-Module & Aufbauplan
  • 08:22 LED-Test mit Adafruit-Library
  • 12:32 Warum RTC statt Systemtakt / millis()?
  • 17:05 Blick auf die Software
  • 34:13 Uhr in Aktion

Code:

/**
 * NeoClock
 * 
 * Clock using 60 WS2812B/Neopixel LEDs and DS3231 RTC
 * 
 * Libraries needed:
 *   * Adafruit NeoPixel (Library Manager) - Phil Burgess / Paint Your Dragon for Adafruit Industries - LGPL3
 *   * Rtc by Makuna (Library Manager) - Michael C. Miller
 *   * Arduino Timezone Library (https://github.com/JChristensen/Timezone) - Jack Christensen - CC-BY-SA
 *   * Time Library (https://github.com/PaulStoffregen/Time) - Paul Stoffregen, Michael Margolis - LGPL2.1
 */

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

#if defined(ESP8266)
#include <pgmspace.h>
#else
#include <avr/pgmspace.h>
#endif

#include <SoftwareWire.h>  // must be included here so that Arduino library object file references work
#include <RtcDS3231.h>

#include <Time.h>        //http://www.arduino.cc/playground/Code/Time
#include <Timezone.h>    //https://github.com/JChristensen/Timezone

#include <EEPROM.h>

//Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};     //Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60};       //Central European Standard Time
Timezone CE(CEST, CET);

TimeChangeRule *tcr;        //pointer to the time change rule, use to get the TZ abbrev
time_t utc;

SoftwareWire myWire(8, 9);
RtcDS3231<SoftwareWire> Rtc(myWire);

#define PIN 6

unsigned long lastMillis = millis();
byte dimmer = 0x88;
byte hmark = 0;

byte ohour=0;
byte ominute=0;
byte osecond=0;

boolean fader=true;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);

void setup() {

  Serial.begin(115200);
  
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

  Rtc.Begin();

  Rtc.Enable32kHzPin(false);
  Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); 

  if (!Rtc.GetIsRunning())
  {
      Serial.println("RTC was not actively running, starting now");
      Rtc.SetIsRunning(true);
  }

  if (!Rtc.IsDateTimeValid()) 
  {
      // Common Cuases:
      //    1) the battery on the device is low or even missing and the power line was disconnected
      Serial.println("RTC lost confidence in the DateTime!");
  }

  byte eechk = EEPROM.read(0);
  if(eechk == 0xAA) { //Assume this is our config and not a fresh chip
    dimmer = EEPROM.read(1);
    hmark = EEPROM.read(2);
    fader = EEPROM.read(3);
  }

  timeSync();
}

void calcTime(void) {
  utc = now();
  CE.toLocal(utc, &tcr);
  ohour = hour(utc);
  ominute = minute(utc);
  if(osecond != second(utc)) {
    osecond = second(utc);
    lastMillis = millis();

    if(ominute == 0 && osecond == 0) {
      //Every hour
      timeSync();
    }
  }
}

void addPixelColor(byte pixel, byte color, byte brightness) {
  color *= 8;
  uint32_t acolor = brightness;
  acolor <<= color;
  uint32_t ocolor = strip.getPixelColor(pixel);
  ocolor |= acolor;
  strip.setPixelColor(pixel, ocolor);
}

void drawClock(byte h, byte m, byte s) {  
  strip.clear();
  
  addPixelColor(m, 1, dimmer);

  if(hmark > 0) {
    for(byte i = 0; i<12; i++) {
      addPixelColor((5*i), 2, hmark);
    }
  }

  h %= 12;
  h *= 5;
  h += (m/12);
  addPixelColor(h, 2, dimmer);
  // 0x RR GG BB

  if(fader) {
    byte dim_s1 = dimmer;
    byte dim_s2 = 0;
    byte px_s2 = s+1;
    if(px_s2 >= 60) px_s2 = 0;
    unsigned long curMillis = millis()-lastMillis;
    if(curMillis < 250) {
      dim_s2 = 0;
      dim_s1 = dimmer;
    }else{
      dim_s2 = map(curMillis, 250, 1000, 0, dimmer);
      dim_s1 = dimmer - map(curMillis, 250, 1000, 0, dimmer);
    }
    
    addPixelColor(s, 0, dim_s1);
    addPixelColor(px_s2, 0, dim_s2);
  }else{
    addPixelColor(s, 0, dimmer);
  }
  
  strip.show();
}

byte rounds = 0;

void loop() {
  calcTime();

  if(rounds++ > 100) {
    Serial.print(ohour);
    Serial.print(":");
    Serial.print(ominute);
    Serial.print(":");
    Serial.println(osecond);
    rounds = 0;
  }
  
  drawClock(ohour,ominute,osecond);

  delay(10);
  
  chkSer();
}

void timeSync(void) {
  RtcDateTime dt = Rtc.GetDateTime();
  setTime(dt.Hour(),dt.Minute(),dt.Second(),dt.Day(),dt.Month(),dt.Year());
    
  Serial.print("Synced to: ");
  Serial.print(dt.Year());
  Serial.print("-");
  Serial.print(dt.Month());
  Serial.print("-");
  Serial.print(dt.Day());
  Serial.print("-");
  Serial.print(dt.Hour());
  Serial.print("-");
  Serial.print(dt.Minute());
  Serial.print("-");
  Serial.println(dt.Second());
}

void timeSave(void) {
  utc = now();
  
  RtcDateTime store = RtcDateTime(year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc));
  Rtc.SetDateTime(store);

  Serial.print("Synced to: ");
  Serial.print(year(utc));
  Serial.print("-");
  Serial.print(month(utc));
  Serial.print("-");
  Serial.print(day(utc));
  Serial.print("-");
  Serial.print(hour(utc));
  Serial.print("-");
  Serial.print(minute(utc));
  Serial.print("-");
  Serial.println(second(utc));
  
}

void setBrightness(byte brightness) {
  dimmer = brightness;
}

void chkSer(void) {
  unsigned int iy;
  byte im,id,iH,iM,iS;
  
  if(!Serial.available()) return;

  switch(Serial.read()) {
    case 'b':
      setBrightness(Serial.parseInt());
      Serial.print(F("Brightness changed to: "));
      Serial.println(dimmer);
      EEPROM.put(0, 0xAA);
      EEPROM.put(1, dimmer);
      break;
    case 't':
      iy = Serial.parseInt();
      im = Serial.parseInt();
      id = Serial.parseInt();
      iH = Serial.parseInt();
      iM = Serial.parseInt();
      iS = Serial.parseInt();
      setTime(iH,iM,iS,id,im,iy);
      Serial.println(F("System time changed"));
      break;
    case 'f':
      fader = false;
      EEPROM.put(0, 0xAA);
      EEPROM.put(3, 0);
      Serial.println(F("Fader off"));
      break;
    case 'F':
      fader = true;
      EEPROM.put(0, 0xAA);
      EEPROM.put(3, 1);
      Serial.println(F("Fader on"));
      break;
    case 'm':
      hmark = Serial.parseInt();
      EEPROM.put(0, 0xAA);
      EEPROM.put(2, hmark);
      Serial.println(F("HMark changed"));
      break;
    case 's':
      timeSync();
      Serial.println(F("Synced RTC to System"));
      break;
    case 'S':
      timeSave();
      Serial.println(F("Synced System to RTC"));
      break;
    default:
      Serial.println('?');
  }
}

Links:

27 Gedanken zu „BitBastelei #234 – WS2812B (Neopixel) Uhr“

  1. Grüße Dich,

    Ich versucher gerade deinen Code zu compalieren, leider spuck mir Arduino einen fehler aus:

    neoclock.ino: In function ‚void chkSer()‘:

    neoclock:242: error: ’setTime‘ was not declared in this scope

    setTime(iH,iM,iS,id,im,iy);

    ^

    exit status 1
    ’now‘ was not declared in this scope

    Die Libs habe Ich Installiert derzeit mache Ich das mit der Aktuellen Arduino Version 1.8.1

    Hast du einen Tipp für mich?

    Grüße

    Andreas

  2. Hallo Florian,

    ich hab Deine Uhr nachgebaut und sie gefällt mir sehr gut .
    Danke dafür.

    Ich habe festgestellt, dass sie pro Minute 3 Sekunden verliert (geht nach). Habe es über einen Sync mit der Echtzeituhr alle 15 Sekunden kompensiert.

    Sie tut aber was sie soll!

    Grüsse
    Jens

    1. Moin moin Jens,

      habe einen eigenen Korpus angfertigt und wollte wissen, ob ich den
      angepassten Sketch (abe es über einen Sync mit der Echtzeituhr alle 15 Sekunden kompensiert.) bekommen kann??

      Grüße

  3. Hallo,
    bin ein begeisterter Bitbastelei Kanal Fan.
    Auch wenn vieleicht off topic aber wie bekomme ich die fünf Minuten LED eingeschalted?
    Vielen dank schonmal im vorraus für die Hilfe
    MfG
    Ulf Thalheim

  4. Vielen Dank für die schnelle Antwort nur was ich nicht Verstehe wie oder wo
    schalte ich die Option ein oder aus? Sorry ich sehe da grad den Wald vor Bäumen nicht. 🙁

    MfG
    Ulf

  5. Moin,
    wo genau kommt der DS3231 an den Uno?
    GND und 5V ist klar, aber die Datenpins?

    Soll spätestens zum 34c3 fertig werden 😉

    Danke
    Sascha

    1. Ich hatte Software-I²C verwendet, da kann so ziemlich jeder Pin genutzt werden – im Beispielcode ist das in Zeile 40 konfiguriert: SDA/Data ist auf 8, SCL/Clock auf 9.

  6. echt toll geworden…
    das meiste kann ich auch nachvollziehen, nur die Einstellung der Farben erschliesst sich mir nicht.
    durch ausprobieren kann ich zwar rot grün und blau zwischen den Zeigern wechseln (0/1/2) aber wie ich zum Beispiel die Stundenmarker auf weiß bekommen soll bleibt mir ein Rätsel.
    genauer gesagt wollte ich die Helligkeit und die Farbe der drei zeiger und der stundenmarker durch Potis verändern können.

    1. Die Farbwerte, die ich bei addPixelColor nutze, ist eine Anzahl von 8-Bit-Shifts. Daraus ergibt sich: 0 = blau, 1 = grün, 2 = rot. Andere Farben sind so nicht möglich, da müsste man die Funktion passend ändern.
      Der Stundenmarker wird aktuell über die Serielle Schnittstelle festgelegt (Zeile 260) – kann man aber auch am Anfang des Programms oder sonstwo passend setzen – analog zu den anderen.

  7. Hallo und danke das du dein projekt teilst.

    ich habe folgendes problem. ich nutze für die uhr eine RTC 1307 und leider spuckt mir dein programm trotz der nutzung der sync funktion „S“ immer wieder die uhrzeit „15:49:10“ aus.

    hättest du einen ansatz woran das liegen kann????

    mfg

    1. Hm – hast du es mal direkt mit den Beispielen versucht? Damit könnte man testen, ob da ein Softwareproblem vorliegt oder das Modul ein Problem hat. Läuft die Uhr immer falsch oder erst nachdem der Strom weg war? Wenn letzteres klingt das nach leerer Batterie.

  8. die uhr läuft mit anderen programmen richtig und behält auch die zeit erst wenn ich diesen sketch aufspiele springt die uhrzeit.
    und wenn es 16 uhr auf der uhr ist fängt sie wieder bei 15:46 an

    ich nutze übrigens einen nano kann das damit zusammenhängern?

    im übrigen funktioniert der sketzt überhaupt erst wenn ich die lib benutze wenn ich die weg lasse kommt folgende:

    C:\Users\philc\Documents\Arduino\xv\xv.ino: In function ‚void calcTime()‘:

    xv:97: error: ’now‘ was not declared in this scope

    utc = now();

    ^

    xv:99: error: ‚hour‘ was not declared in this scope

    ohour = hour(utc);

    ^

    xv:100: error: ‚minute‘ was not declared in this scope

    ominute = minute(utc);

    ^

    xv:101: error: ’second‘ was not declared in this scope

    if(osecond != second(utc)) {

    ^

    C:\Users\philc\Documents\Arduino\xv\xv.ino: In function ‚void timeSync()‘:

    xv:184: error: ’setTime‘ was not declared in this scope

    setTime(dt.Hour(),dt.Minute(),dt.Second(),dt.Day(),dt.Month(),dt.Year());

    ^

    C:\Users\philc\Documents\Arduino\xv\xv.ino: In function ‚void timeSave()‘:

    xv:201: error: ’now‘ was not declared in this scope

    utc = now();

    ^

    xv:203: error: ‚year‘ was not declared in this scope

    RtcDateTime store = RtcDateTime(year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc));

    ^

    xv:203: error: ‚month‘ was not declared in this scope

    RtcDateTime store = RtcDateTime(year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc));

    ^

    xv:203: error: ‚day‘ was not declared in this scope

    RtcDateTime store = RtcDateTime(year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc));

    ^

    xv:203: error: ‚hour‘ was not declared in this scope

    RtcDateTime store = RtcDateTime(year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc));

    ^

    xv:203: error: ‚minute‘ was not declared in this scope

    RtcDateTime store = RtcDateTime(year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc));

    ^

    xv:203: error: ’second‘ was not declared in this scope

    RtcDateTime store = RtcDateTime(year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc));

    ^

    C:\Users\philc\Documents\Arduino\xv\xv.ino: In function ‚void chkSer()‘:

    xv:246: error: ’setTime‘ was not declared in this scope

    setTime(iH,iM,iS,id,im,iy);

    ^

    exit status 1
    ’now‘ was not declared in this scope

  9. damit startet übrigens der serielle monitor

    RTC was not actively running, starting now
    RTC lost confidence in the DateTime!
    Synced to: 2009-85-165-37-165-85

  10. ersteinmal danke für deine hilfe ich habe den fehler gefunden,

    es lag erstmal nicht an der RTC ich musste den code an die Lib für die RTC1307 etwas anpassen und die Softwarewire pins umsetzen und dann ging es.

    danke das du uns an deinem projekt hast teilhaben lassen.

    mfg

  11. hallo, der sktech läuft auf meinem UNO ohne probleme, wenn ich das aber auf einen esp8266 aufspielen will kommt die fehlermeldung das Timezone.h nicht für den esp8266 ist, habe mitlerweile gelesen das der esp8266 nicht mehr unterstützt wird. kannst du etwas dazu sagen, desweiteren wäre es schön wenn man von NTP die zeit holen könnte.

    danke im voraus für die antwort

  12. Hallo Adlerweb,
    habe deine Software heruntergeladen, alle Libs installiert und mit der Arduino IDE 1.8.12 auf einem Elegoo Uno R3 hochgeladen. Es sind keine Fehlermeldungen aufgetreten. Der Serielle Monitor zeigt mir im Sekundentakt die leider falsche Zeit an. Habe noch keine Möglichkeit gefunden diese zu ändern. Auch alle andere Einstellungen per seriellem Monitor werden nicht angezeigt. Wo liegt der Fehler, oder bin ich nicht in der Lage deine doch ausführliche Anleitung zu verstehen.
    Ich bitte um Hilfe.
    Vielen Dank im voraus.
    Gruß
    Norbert Schrills

    1. Soweit ich mich entsinne ist bei mir nicht zum Stellen der Zeit drin. Schau mal in der Library, da müsste ein Beispielcode dabei sein. Wenn damit die Zeit einmal gesetzt ist sollte sie – do lange die RTC noch Strom bekommt oder dessen Batterie voll ist – auch in der LED-Uhr dann richtig sein.

  13. Hallo Adlerweb,

    könnten Sie mir bei einem Problem mit Ihrem Arduino Programm helfen denn, ich kann die Uhrzeit nicht ändern und ich kann auch nicht die Stundenmarkierung einschalten, in dem Seriellen Monitor bekomme ich nur ? ausgegeben.

    Mit freundlichen Grüßen

    Jonas Brehm

  14. Hallo Adlerweb könnten sie mir bei einem Problem mit ihrem Programm helfen. Vielen Dank. Bei mir kommen nur „?“

    1. ? heißt, dass er die Eingabe nicht interpretieren konnte. Welche Zeichen wurden denn gesendet? Vielleicht einfach mal nach dem Einschalten „m“ senden und schauen, ob das geht?

  15. Hallo,
    ich hätte eine Frage. Und zwar nutze ich den DS1307 anstatt den Ds3231, nun komme ich aber zu folgendem Problem. Ich möchte die Uhrzeit am Anfang des Sketches einmal festlegen und dann ausklammern, damit der DS1307 überhaupt einmal eine vernünftige Zeit hat. Wie kann ich das umsetzen?
    Vielen Dank schon einmal!

  16. Ich habe die Uhr Anfang letzten Jahres (2023) nachgebaut und etwas angepasst. Unter anderem habe ich einen LDR mit eingebaut, um die Uhr bei Dunkelheit zu dimmen. Als Gehäuse habe ich einfach eine defekte LED-Deckenleuchte verwendet und alles hineingebastelt.
    Bis vor ein paar Wochen hing die Uhr im Zimmer meiner Tochter, doch mittlerweile ist ihr diese selbst bei kleinster Helligkeitseinstellung nachts zu hell und die Uhr liegt wieder in der Bastelkiste. Bis dahin hat sie aber perfekt funktioniert und auch das Umstellen auf Sonner/Winterzeit über USB ging jedes mal einwandfrei.

    Ich denke, ich werde die Uhr, wenn ich ein Wenig Zeit habe, etwas modifizieren und den Arduino gegen einen ESP8266 tauschen, der sich die Zeit von einem Zeitserver holen kann und sie dann ins Wohnzimmer hängen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert